<?php
/**
 * Created by laoyu.
 * User: hilaoyu@qq.com
 * Date: 2018/7/15
 * Time: 上午10:17
 */

namespace Hilaoyu\Utils;



class UtilFile
{
    static public function checkDir($dir)
    {
        UtilSupport::tryQuiet(function () use ($dir) {
            !is_dir($dir) && mkdir($dir, 0755, true);
        });


        return is_dir($dir) && is_writable($dir);
    }

    /**
     * 格式化文件大小
     * @param $fileSize
     * @return string
     */
    public static function sizeFormat($fileSize)
    {
        $size = sprintf("%u", $fileSize);
        if ($size == 0) {
            return ("0 Bytes");
        }
        $sizename = array(" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB");
        return round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . $sizename[$i];
    }


    public static function getExtension($fileName = '')
    {
        $fileName = trim($fileName);
        return pathinfo($fileName, PATHINFO_EXTENSION);
    }

    public static function getNameWithOutExtension($fileName = '')
    {
        $fileName = trim($fileName);
        $ext = static::getExtension($fileName);

        return rtrim(UtilStr::replaceLast($ext, '', $fileName), '.');
    }

    public static function dirname($filename)
    {
        $filename = UtilStr::clearRepeatedChar($filename, ['/', '\\'], '/');
        return dirname($filename);
    }

    public static function basename($filename)
    {
        return preg_replace('/^.+[\\\\\\/]/', '', $filename);
    }

    public static function hash($path, string $algo = 'md5')
    {
        return hash_file($algo, $path);
    }

    static public function delDirOrFile(string $path, \Closure $filter = null)
    {
        $Ex = true;
        try {
            if (is_file($path)) {
                //dump('1:'.$path);
                if (!is_callable($filter) || (is_callable($filter) && $filter($path))) {
                    //dump('1rm:'.$path);
                    $Ex = @unlink($path);
                }

            } elseif (is_dir($path)) {
                //dump('2:'.$path);
                $open_dir = opendir($path);
                $Ex = true;
                while (($file = readdir($open_dir)) != false) {
                    if ($file == '.' || $file == '..') {
                        continue;
                    }
                    $file_name = rtrim($path,'/') . '/' . ltrim($file,'/');
                    //dump('22:'.$file_name);
                    $Ex = $Ex && static::delDirOrFile($file_name, $filter);
                }
                @closedir($open_dir);

                if (!is_callable($filter) || (is_callable($filter) && $filter($path))) {
                    //dump('2rm:'.$path);
                    $Ex = $Ex && @rmdir($path);
                }

            }
        } catch (\Exception $e) {
            //dump($e);
            $Ex = false;
        }


        return $Ex;
    }

    /**
     * 复制目录或文件
     * @param $src 源
     * @param $dist 目标
     * @return bool
     */
    static public function copyDirOrFile($src, $dist)
    {
        try {

            if (is_file($src)) {
                $src_file_name = UtilFile::basename($src);

                if (in_array(substr($dist, -1), ['/', '\\'])) {
                    $dist = $dist . $src_file_name;
                }

                $dist_dir = dirname($dist);
                if (!static::checkDir($dist_dir)) {
                    return false;
                }
                //dump($src,$dist);
                return @copy($src, $dist);
            }

            if (is_dir($src)) {
                $open_dir = opendir($src);
                $Ex = true;
                while (($file = readdir($open_dir)) != false) {
                    if ($file == '.' || $file == '..') {
                        continue;
                    }

                    $file_name = $src . '/' . $file;
                    $to = $dist . '/' . $file;
                    if (!static::copyDirOrFile($file_name, $to)) {
                        @closedir($open_dir);
                        throw new \Exception('复制文件:' . $file_name . ' 到:' . $to . ' 失败');
                    }
                }

                @closedir($open_dir);

                return $Ex;
            }
        } catch (\Exception $e) {
            //dump($e->getMessage());
            return false;
        }


        return false;
    }

    static public function dirNameToHash($dir, $level = 3)
    {
        $dir = trim($dir);
        $dir = ltrim($dir, '/');
        $dir = ltrim($dir, '\\');
        $level = intval($level);

        if ('' == $dir) {
            return $dir;
        }
        $org_dir = $dir;


        $paths = [];
        for ($i = 1; $i < $level; $i++) {
            if (!$dir) {
                break;
            }
            if ($path = UtilStr::substr($dir, 0, 2)) {
                $paths[] = $path;
                $dir = UtilStr::substr($dir, UtilStr::length($path));
            }
        }

        $paths[] = $org_dir;

        $path = implode(DIRECTORY_SEPARATOR, $paths);
        $path = UtilStr::clearRepeatedChar($path, ['/', '\\'], DIRECTORY_SEPARATOR);
        return $path;
    }

    /**
     * 扫描目录下的文件
     * @param $dir
     * @param null $callback
     * @return array
     */
    static public function scanFiles($dir, $callback = null)
    {
        $files = [];

        if (is_dir($dir)) {
            $open_dir = opendir($dir);
            while (($file = readdir($open_dir)) != false) {
                if ($file == '.' || $file == '..') {
                    continue;
                }

                $file_name = $dir . '/' . $file;
                $files = array_merge($files, static::scanFiles($file_name, $callback));
            }

            @closedir($open_dir);
        } else {
            if ($callback instanceof \Closure) {
                call_user_func($callback, $dir);
            }
            $files[] = $dir;
        }

        return $files;
    }


    /**
     * 压缩成zip
     * @param $zip_path  生成的zip的路径
     * @param $path 要压缩的目录或文件
     * @param string $local_path 在zip中的路径
     * @param bool $overwrite 如果压缩包已存在是否覆盖
     * @return bool|string
     */
    static public function compressZip($zip_path, $path, $password = '', $local_path = '/', $overwrite = true)
    {
        try {
            $zip = new \ZipArchive();
            $flags = \ZipArchive::CREATE;
            if ($overwrite) {
                $flags = $flags | \ZipArchive::OVERWRITE;
            }
            $ret = $zip->open($zip_path, $flags);
            if ($ret !== TRUE) {
                throw new \Exception('Failed with code %d', $ret);
            }
            if ($password) {
                $zip->setPassword($password);
            }

            if (is_dir($path)) {
                /*if(!in_array(substr($local_path,-1),['/','\\'])){
                    $local_path .= '/';
                }*/

                UtilFile::scanFiles($path, function ($file) use ($zip, $local_path, $path, $password) {

                    $local_file = UtilStr::clearRepeatedChar($local_path . '/' . substr($file, strlen($path)), ['\\', '/'], '/');
                    $local_file = ltrim($local_file, '/');

                    $zip->addFile($file, $local_file);
                    if ($password) {
                        UtilSupport::tryQuiet(function () use (&$zip, $local_file) {
                            $zip->setEncryptionName($local_file, \ZipArchive::EM_AES_256);
                        });
                    }
                });

            } else {
                if (in_array(substr($local_path, -1), ['/', '\\'])) {
                    $local_path .= UtilFile::basename($path);
                }
                $local_file = UtilStr::clearRepeatedChar($local_path, ['\\', '/'], '/');
                $local_file = ltrim($local_file, '/');
                $zip->addFile($path, $local_file);
                if ($password) {
                    UtilSupport::tryQuiet(function () use (&$zip, $local_file) {
                        $zip->setEncryptionName($local_file, \ZipArchive::EM_AES_256);
                    });

                }
            }


            $zip->close();

            return true;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
        return false;
    }


    /**
     * 解压zip文件
     * @param $zip_path
     * @param $to
     * @param $password
     * @return bool
     */
    public static function extractZip($zip_path, $to, $password = '')
    {

        $zip_path = trim($zip_path);
        $to = trim($to);

        if (!file_exists($zip_path)) {
            return false;
        }

        if (empty($to) || !UtilFile::checkDir($to)) {
            return false;
        }

        try {
            $cmd_output = '';
            $unar_has = UtilCli::execute('unar -v', $cmd_output);
            //dump($unar_has);
            if ($unar_has) {

                $extractCmd = "unar {$zip_path} -o {$to} -f -D";
                $extractCmd .= " -e gbk"; //中文文件名乱码
                if ($password) {
                    $extractCmd .= ' -p ' . $password;
                }

                $status = UtilCli::execute($extractCmd, $cmd_output);
                //dd($extractCmd,$status);
                if (!$status) {
                    throw new \Exception($cmd_output);
                }
                return true;
            }

            $zip = new \ZipArchive();
            if ($zip->open($zip_path) === TRUE) {//中文文件名要使用ANSI编码的文件格式

                if ($password) {
                    $zip->setPassword($password);
                }
                $status = $zip->extractTo($to);//提取全部文件

                //$zip->extractTo('/my/destination/dir/', array('pear_item.gif', 'testfromfile.php'));//提取部分文件
                $zip->close();

                if (!$status) {

                    throw new \Exception('解压失败');
                }
                return true;
            } else {
                throw new \Exception('解压目录不可写');
            }
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }


    static public function typeName($type = '', $default = null)
    {
        $type = trim($type);
        $types = [
            'image' => '图片',
            'video' => '视频',
            'audio' => '音频',
            '_other' => '其它',
        ];

        if ($type) {
            return UtilArr::get($types, $type, $default);
        }

        return $types;
    }


    static public function mineToNormalType($mimeType)
    {
        $type = UtilStr::before($mimeType, '/');
        if (UtilFile::typeName($type)) {
            return $type;
        }

        return '_other';
    }

    static public function mimeType($fileName)
    {
        if (phpversion() >= '5.3.0') {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($finfo, $fileName);
            finfo_close($finfo);
        } else {
            $mimeType = mime_content_type($fileName);
        }
        return $mimeType;

    }
}
